// use crate::model::Fund;
use anyhow::anyhow;
use anyhow::Ok;
use anyhow::Result;
use surrealdb::opt::PatchOp;

use crate::db::FUDB;
use crate::db::FUHLC;
use crate::model::trade::trade_holding;
use crate::model::trade::trade_holding::TradeHolding;
use crate::model::FudaRecord;

///
/// update or create a trade_holding record
///
/// @param trade_holding: trade_holding's basic information
///
pub async fn upsert(tpos: &trade_holding::TradeHolding) -> Result<Option<FudaRecord>> {
    let id = trade_holding::TradeHolding::id_from(&tpos.holding_id)?;

    let old_tpos: Option<trade_holding::TradeHolding> = FUDB.select((
        "trade_holding",
        id.as_str(),
    )).await?;

    let mut updated: Option<FudaRecord> = None;
    let mut to_update = false;
    let timestamp = FUHLC.new_timestamp().to_string();
    // Update a record with a specific ID
    let mut patch = FUDB.update(("trade_holding", &id)).patch(
        PatchOp::replace("/last_updated", timestamp)
    );

    match old_tpos {
        Some(TradeHolding { id: Some(t), .. }) => {
            updated = Some(FudaRecord { id: t.into() });
        }
        _ => {
            patch = patch.patch(PatchOp::replace("/holding_id", &tpos.holding_id));

            to_update = true;
        }
    }

    if let Some(v) = &tpos.stop_price {
        patch = patch.patch(PatchOp::replace("/stop_price", v));
        to_update = true;
    }

    if let Some(v) = &tpos.take_profit_price {
        patch = patch.patch(PatchOp::replace("/take_profit_price", v));
        to_update = true;
    }

    if let Some(v) = &tpos.data {
        patch = patch.patch(PatchOp::replace("/data", v));
        to_update = true;
    }

    if to_update {
        updated = patch.await.map_err(|err| anyhow!(err))?;
    }

    Ok(updated)
}

///
/// delete trade_holding by it's id , e.g. SH.STK.600519
///
pub async fn delete(trade_holding_id: &str) -> Result<trade_holding::TradeHolding> {
    let rs: Option<trade_holding::TradeHolding> = FUDB.delete((
        "trade_holding",
        trade_holding_id,
    )).await?;
    match rs {
        Some(trade_holding) => Ok(trade_holding),
        None =>
            Err(
                anyhow!(
                    format!("Failed to delete, TradePosition not found for {}", trade_holding_id)
                )
            ),
    }
}

/// select trade_strategies
///
/// klt: [Klt](super::kline::Klt)
///
/// pi: u16, page index, start from 0
///
/// pn: u16, page size,
///
pub async fn select(
    holding_id: Option<String>,
    pi: u16,
    pn: u16
) -> Result<Vec<trade_holding::TradeHolding>> {
    let start = pi * pn;

    let q_str = match &holding_id {
        Some(_) =>
            format!(
                "SELECT * FROM trade_holding WHERE holding_id=$holding_id LIMIT {} START {}",
                pn,
                start
            ),
        None => format!("SELECT * FROM trade_holding LIMIT {} START {}", pn, start),
    };

    // Fetch kline data within the specified date range using bind variables
    let mut query = FUDB.query(q_str);

    if let Some(t_) = &holding_id {
        query = query.bind(("holding_id", t_)); // Bind the start_date variable
    }

    // println!("query={:?}", query);

    let mut response = query.await?;

    // println!("\nresponse= {:?}\n", response);

    let values: Vec<trade_holding::TradeHolding> = response.take(0)?;

    Ok(values)
}

#[cfg(test)]
mod test {
    use crate::db;
    use crate::db::FUHLC;
    use crate::model::trade::trade_holding;
    use crate::model::trade::trade_holding_data::Money;
    use crate::model::trade::trade_holding_data::Share;
    use crate::model::trade::trade_holding_data::TradeHoldingData;
    use crate::model::trade::TRADE_HOLDING_ID_MONEY_CNY;

    #[tokio::test]
    async fn test_upsert_trade_holding() {
        let _ = db::init().await.unwrap();

        let money = TradeHoldingData::Money(Money {
            currency: "CNY".to_string(),
            total: 1000.0,
            available: 200.0,
            asset_total: 500.0,
            toady_profit: 300.0,
            holding_profit: 600.0,
        });

        let tpos = trade_holding::TradeHolding {
            holding_id: format!("{}", TRADE_HOLDING_ID_MONEY_CNY),
            data: Some(money.clone()),
            ..Default::default()
        };

        // Update a trade_holding record with a specific id
        let res = db::trade::trade_holding::upsert(&tpos).await;

        println!("res {:?}", res);

        for i in 0..3 {
            let share = TradeHoldingData::Share(Share {
                security_name: format!("贵州茅台{}", i),
                holding_percentage: 18f64,
                profit_loss_amount: 123.0,
                profit_loss_percentage: 123.0,
                market_value: 123.0,
                current_price: 123.0,
                cost_basis: 123.0,
                shares_held: 123,
                available_shares: 123,
            });
            let tpos = trade_holding::TradeHolding {
                holding_id: format!("SH.STOCK.6{:05}", i),
                data: Some(share.clone()),
                ..Default::default()
            };

            // Update a trade_holding record with a specific id
            let res = db::trade::trade_holding::upsert(&tpos).await;

            println!("res {:?}", res);
        }
    }

    /// Need to call first: test_update_kline_from_eastmoney to update klines for "513130"
    #[tokio::test]
    async fn test_select_trade_holdings() {
        // generate a timestamp
        let ts = FUHLC.new_timestamp();
        println!("\nts = {}", ts.to_string());

        let _ = db::init().await.unwrap();

        let result = db::trade::trade_holding::select(None, 0, 10).await;

        println!("result= {:?}\n", result);
        assert_eq!(result.is_ok(), true);

        if let Ok(values) = &result {
            for (index, value) in values.iter().enumerate() {
                println!("\n[{:02}] value={:?}, ", index, value);
            }
        }
    }
}
